﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;

namespace PortableLog
{
    public class Logger
    {
        private string module;
        private LogManage manage;
        private StorageFolder folder;
        private StorageFile file;

        public Logger(LogManage manage, string moduleName)
        {            
            this.module = moduleName;
            this.manage = manage;
            InitiateLogger();
        }

        private async void InitiateLogger()
        {
            await CreateLogFolder();   
        }

        private async Task CreateLogFolder()
        {
            folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Log",
                CreationCollisionOption.OpenIfExists);

            await CreateLogFile();
        }

        private async Task CreateLogFile()
        {
            if(null == file)
            {
                file = await folder.CreateFileAsync(module + ".log", CreationCollisionOption.OpenIfExists);
            }

            Windows.Storage.FileProperties.BasicProperties property =
                await file.GetBasicPropertiesAsync();

            if(property.Size > manage.MaxLogFileSize * 1024 * 1024)
            {
                await file.RenameAsync(module + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".log");
                file = await folder.CreateFileAsync(module + ".log", CreationCollisionOption.OpenIfExists);
                DeleteLogFile();
            }
        }

        private async void DeleteLogFile()
        {
            try
            {
                var logFiles = await folder.GetFilesAsync();
                if (logFiles.Count > manage.MaxLogFileCount)
                {
                    DateTimeOffset offset = DateTime.Now;
                    StorageFile oldFile = null;
                    foreach (var logFile in logFiles)
                    {
                        if (logFile.DateCreated < offset)
                        {
                            oldFile = logFile;
                        }
                    }

                    if(null != oldFile)
                    {
                        await oldFile.DeleteAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                WriteCriticalLog(ex.Message);
            }
        }

        public async void WriteLog(LoggingLevel level, string format, params object[] args)
        {
            if (LoggingLevel.Verbose == level && false == manage.VerboseLogEnable)
                return;

            string log = string.Format("{0} {1} {2}{3}", 
                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffzzz"), 
                level, 
                string.Format(format,args),
                Environment.NewLine);
            
            if (file != null)
            {
                await FileIO.AppendTextAsync(file, log);
            }

            await CreateLogFile();
        }

        public void WriteVerboseLog(string format, params object[] args)
        {
            WriteLog(LoggingLevel.Verbose, format, args);
        }

        public void WriteInformationLog(string format, params object[] args)
        {
            WriteLog(LoggingLevel.Information, format, args);            
        }

        public void WriteWarningLog(string format, params object[] args)
        {
            WriteLog(LoggingLevel.Warning, format, args);
        }

        public void WriteErrorLog(string format, params object[] args)
        {
            WriteLog(LoggingLevel.Error, format, args);
        }

        public void WriteCriticalLog(string format, params object[] args)
        {
            WriteLog(LoggingLevel.Critical, format, args);
        }
                
        public async void PackageLogToZipFile(StorageFile zipFile)
        {
            try
            {                
                if (zipFile != null)
                {
                    Stream zipStream = await zipFile.OpenStreamForWriteAsync();
                    using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
                    {
                        var logFiles = await folder.GetFilesAsync();
                        foreach (var logFile in logFiles)
                        {
                            ZipArchiveEntry readmeEntry = archive.CreateEntry(logFile.Name);
                            Stream logStream = await logFile.OpenStreamForReadAsync();
                            logStream.CopyTo(readmeEntry.Open());
                        }
                    }
                }                
            }
            catch (Exception ex)
            {
                WriteCriticalLog(ex.Message);
            }
        } 
    }
}
